异或运算常见的应用
The following article is from Linux开发那些事儿 Author LinuxThings
大家可能比较熟悉 "与" 运算 和 "或" 运算 ,相对而言, "异或" 运算 平常使用较少,存在感也不强,如果不是刻意提起,可能还想不到它
其实,"异或" 运算也非常重要,它在加密、备份、算法等方面都有应用,每一位开发的同学都应该花点儿时间掌握它的特点和规律,以便在日常工作中能灵活的运用
接下来将介绍异或运算的一些基础知识以及在实际中的一些应用
基础知识
异或是计算机中一种二元逻辑运算, 运算符号是 ^,它按照二进制位进行异或运算,结果为 真 或 假, 它的运算法则如下
x | y | x^y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
表格 第一列 和 第二列 是异或运算的两个操作数,第三列是异或运算的结果,1 表示真,0 表示假
由表格可知:如果参与运算的两个二进制位相同,则结果为 0 ,否则为 1
也就是说,异或主要用来判断两个值是否相同
重要的性质
下面列出异或运算一些重要的性质,1 表示真,0 表示假, 具体的验证过程比较简单,这里就省略了
1、一个数与自身异或,总是为 0
x ^ x = 0
2、 一个数与 0 异或,总是其自身
x ^ 0 = x
3、 交换性
x ^ y = y ^ x
4、 结合性
x ^ ( y ^ z ) = ( x ^ y ) ^ z
常见应用
异或运算本身比较简单,重点还是在应用层面,上面列出的性质,在很多方面都有应用
判断两个数是否相等
一个数与自身异或,总是为 0,我们可以使用这一点来判断两个变量是否相等
( a ^ b ) == 0
当 a
和 b
相等时,表达式为真,否则为假
不使用临时变量交换两个数的值
要交换两个数的值,通常做法是借助一个临时变量,然后再进行交换,比如:tmp
是临时变量,现需要交换 a
和 b
两个变量值,过程如下
tmp = a
a = b
b = tmp;
利用异或的一些性质,不用临时变量也能实现交换两个数的值,具体过程如下
a = a ^ b
b = a ^ b
a = a ^ b
假如初始时,a = 1、b = 2
第一个等式 a = a ^ b
结果是 a = 1 ^ 2 = 3
紧接着第二个等式 b = a ^ b
结果是 b = 1 ^ 2 ^ 2 = 1 ^ 0 = 1
最后一个等式 a = a ^ b
结果是 b = 1 ^ 2 ^ 1 = 1 ^ 1 ^ 2 = 0 ^ 2 = 2
可以看到,最终 a = 2、 b = 1
,它们的值实现了交换
上面的三条语句还可以进一步优化成一条,结果如下
a ^= b ^= a ^= b
简化表达式
根据交换性,可以优化表达式中重复变量的异或运算,比如:表达式 a ^ b ^ c ^ a ^ b
可以做如下简化
a ^ b ^ c ^ a ^ b # 根据 x ^ y = y ^ x
= ( a ^ a ) ^ ( b ^ b ) ^ c # 根据 x ^ x = 0
= 0 ^ 0 ^ c # 根据 x ^ 0 = x
= c
加密
利用异或运算加密是很常见的加密手段,它涉及到三个变量:明文、密钥、密文,假如它们分别记为 plain_text、 encrypt_key、 cipher_text
明文和密钥进行异或运算,可以得到密文
plain_text ^ encrypt_key = cipher_text
密文和密钥进行异或运算,可以得到明文
cipher_text ^ encrypt_key
= ( plain_text ^ encrypt_key ) ^ encrypt_key
= plain_text ^ ( encrypt_key ^ encrypt_key ) # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y
= plain_text ^ 0 # 根据 x ^ x = 0
= plain_text
备份
根据异或的性质,异或运算还可以用于文件的备份
现有两个文件 filea
和 fileb
,它们进行异或运算,会产生一个新的备份文件 bakfile
bakfile = filea ^ fileb
根据异或的性质,可以通过 bakfile
和 filea
得到 fileb
,或者通过 bakfile
和 fileb
得到 filea
后面无论是 filea
或 fileb
文件损坏了,只要不是两个文件同时损坏,都可以通过两者中未损坏的文件 和 bakfile
文件,还原得到另一个文件
当 filea
文件损坏了,可以通过 fileb
和 bakfile
进行异或运算得到完好的 filea
文件
fileb ^ bakfile
= fileb ^ ( filea ^ fileb )
= fileb ^ filea ^ fileb # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y
= fileb ^ fileb ^ filea # 根据 x ^ x = 0
= 0 ^ filea # 根据 x ^ 0 = x
= filea
同样,当 fileb
文件损坏了,可以通过 filea
和 bakfile
进行异或运算得到完好的 fileb
文件
filea ^ bakfile
= filea ^ ( filea ^ fileb )
= filea ^ filea ^ fileb # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y
= filea ^ filea ^ fileb # 根据 x ^ x = 0
= 0 ^ fileb # 根据 x ^ 0 = x
= fileb
解算法题
有些算法可以利用异或的思路进行求解,下面列出力扣上的一道算法题来说明,题目如下:
一个长度为 n-1 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 0 ~ n-1 之内,
在范围 0 ~ n-1 内的 n 个数字中有且只有一个数字不在该数组中,请找出这个数字
示例 1:
输入: [ 0,1,3 ]
输出: 2
示例 2:
输入: [ 0,1,2,3,4,5,6,7,9 ]
输出: 8
最快捷的解决方法是把数组的所有元素以及 0
到 n-1
之间的整数 全部进行异或运算
arry[0] ^ arry[1] ^ arry[2] ... ^ arry[n-2] ^ 0 ^ 1 ^ 2 .... ^ (n-1)
由于数组元素值范围在 0
到 n-1
,且所有元素值都没有重复
所以,上述的计算式子中,0
到 n-1
每个数会出现两次,只有缺少的那个数仅出现一次,根据异或的性质 x ^ x = 0
可知,相同值之间的异或运算等于 0
,因此算式的结果其实就是缺少的那个数
下面给出测试文件 test.cpp
,代码是用 C++ 实现的,可以自行用其他语言实现
#include <stdint.h>
#include <iostream>
using namespace std;
int32_t find_missing(int32_t *ary, int32_t len)
{
//数组长度小于等于1,直接返回 -1
if(len <= 1)
{
return -1;
}
//结果
int32_t result = 0;
//result 和 数组中每一个元素值进行异或运算
for (int32_t i = 0; i < len; i++)
{
result ^= ary[i];
}
//result 和 0 到 n 中每一个值进行异或运算
for (int32_t j = 0; j <= len; j++)
{
result ^= j;
}
return result;
}
//编译: g++ -g -o test test.cpp
int32_t main(int32_t argc , char *argv[])
{
int32_t ary[] = { 0, 1, 3 };
int32_t result = find_missing(ary, sizeof(ary) / sizeof(int32_t));
std::cout << "result = " << result << std::endl;
return 0;
}
使用 g++ -g -o test test.cpp
命令编译,接着运行程序,结果如下:
[root@localhost test]# ./test
result = 2
当然,这道题目还有其他的解法,比如:利用加法来解,大家自己去思考下,这里不做介绍了
小结
通过上文,我们可以看到,异或运算在很多方面都有应用,其实,它的应用远不止文中介绍的方面,所以,我们花时间去掌握它是非常值得做的一件事
- EOF -
关注『CPP开发者』
看精选C++技术文章 . 加C++开发者专属圈子
点赞和在看就是最大的支持❤️